<style scoped lang="scss">
.main {
  padding: 2vh;
  background-color: #2d3748;
}
.n-card {
  width: 20vw;
}
.n-slider {
  margin-top: 2vh;
}
.coordinate {
  margin-top: 2vh;
}
.n-input {
  margin-top: 1vh;
}
</style>
<template>
  <div class="main">
    <n-flex justify="space-around" size="large">
      <div>
        <n-scrollbar style="max-height: 96vh">
          <canvas ref="canvas" width="1371" height="1920" :style="canvasStyle"></canvas>
        </n-scrollbar>
      </div>
      <div>
        <n-card>
          <n-switch v-model:value="isDrawingEnabled" :rail-style="railStyle">
            <template #checked>开启绘画</template>
            <template #unchecked>调整绘画</template>
          </n-switch>

          <n-space vertical>
            <n-slider v-model:value="value" :step="1" @update:value="handleSwitchChange" />
          </n-space>
        </n-card>
        <n-card style="margin-top: 3vh">
          <n-flex justify="space-between">
            <n-a href="/dot"> 点画框 </n-a>
            <n-a href="/line"> 拖动画框 </n-a>
            <n-a href="/surface"> 指定画框 </n-a>
          </n-flex>
        </n-card>
        <n-card style="margin-top: 3vh">
       
          <div class="coordinate">
         
            <n-input
              placeholder="音频名称"
              v-for="(shape, shapeIndex) in coordinates"
              :key="shapeIndex"
              :default-value="shape.audio"
              @mouseenter="handleMouseEnter(shapeIndex)"
              @mouseleave="handleMouseLeave(shapeIndex)"
              @update:value="(val: string) => (shape.audio = val)"

            >
              <template #prefix>
                <n-icon>
                  <ChatboxOutline />
                </n-icon>
              </template>
              <template #suffix>
                <n-button text style="font-size: 15px">
                  <n-icon @click="playAudio(shape.audio)">
                    <PlayOutline />
                  </n-icon>
                </n-button>
              </template>
            </n-input>
          </div>
        </n-card>
        <n-card style="margin-top: 3vh">
          <n-pagination
            mt5vh
            v-model:page="page"
            :page-count="imageList.page.total_page"
            @update:page="onPageChange"
          />
        </n-card>
      </div>
    </n-flex>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch, computed } from 'vue'
import { ChatboxOutline, PlayOutline } from '@vicons/ionicons5'

import type { CSSProperties } from 'vue'
import graphicalData from '@/store/tool/graphicalData'
/* -- 数据 -- */
import { getPdfImages, type PdfImagesResponse } from '@/api/image'
const coordinates = ref([])
const props = defineProps<{
  id: string
}>()
const pdfId = Number(decodeURIComponent(props.id))
const page = ref(1)
const imageList = ref<PdfImagesResponse>({
  data: [],
  page: { current_page: 1, total_page: 1 }
})
const onPageChange = (newPage: number) => {
  newPage = page.value
}
watch(page, (newPage) => {
  fetchData(newPage)
})

/* -- 数据end -- */
const appGaphicalData = graphicalData()

interface Point {
  x: number
  y: number
}

//切换画框类型

let currentMousePos = ref<{ x: number; y: number } | null>(null)
//启用水平垂直状态
const showGuidelines = ref(true)

const railStyle = ({ focused, checked }: { focused: boolean; checked: boolean }): CSSProperties => {
  const style: CSSProperties = {}
  if (checked) {
    style.background = '#2080f0'

    if (focused) {
      style.boxShadow = '0 0 0 2px #2080f040'
    }
  } else {
    style.background = '#d03050'
    if (focused) {
      style.boxShadow = '0 0 0 2px #d03050'
    }
  }
  return style
}

// 判断音频是否可用


const playAudio = (url: string) => {
  const audio = new Audio(url)
  audio
    .play()
    .then(() => {
    })
    .catch((error) => {
       alert('音频错误')
    })
}

const isDrawingEnabled = ref(true)
const value = ref(100)

const canvasStyle = computed(() => {
  const imageUrl = imageList.value.data.length > 0 ? imageList.value.data[0].url : ''
  return {
    backgroundImage: `url('${imageUrl}')`,
    transform: `scale(${value.value / 100})`,
    transformOrigin: 'top center'
  }
})
//缩放预览时 退出绘画模式
function handleSwitchChange() {
  if (isDrawingEnabled.value) {
    isDrawingEnabled.value = false
  }
}
//切换模式时候需要恢复原屏幕
watch(isDrawingEnabled, () => {
  if (isDrawingEnabled.value === true && value.value !== 100) {
    const confirmChange = confirm('当前绘画比例不是 100%。是否要将其调整为 100% 并继续绘画？')
    if (confirmChange) {
      value.value = 100
      isDrawingEnabled.value = true
    } else {
      isDrawingEnabled.value = false
    }
    isDrawingEnabled.value = true
  }
})

const hoveredShapeIndex = ref<number | null>(null)

const canvas = ref<HTMLCanvasElement | null>(null)
const ctx = ref<CanvasRenderingContext2D | null>(null)
const shapes = ref<{ points: { x: number; y: number }[]; isClosed: boolean }[]>([])

let currentShape = ref<{ points: { x: number; y: number }[]; isClosed: boolean } | null>(null)

let isDraggingPoint = ref(false)
let isDraggingShape = ref(false)
let draggedPoint = ref<{ shapeIndex: number; pointIndex: number } | null>(null)
let draggedShapeIndex = ref<number | null>(null)
let lastMousePos = ref<{ x: number; y: number } | null>(null)
let isDrawingLine = ref(false) // 控制虚线的绘制

const fetchData = async (pageNumber: number) => {
  try {
    const response = await getPdfImages(pdfId, pageNumber)

    imageList.value = response.data
    coordinates.value = response.data.data[0].coordinate
    if (canvas.value) {
      ctx.value = canvas.value.getContext('2d')
      shapes.value = coordinates.value.map((shape, index) => ({
        shapeIndex: shape.shapeIndex,
        audio: shape.audio,
        points: shape.points.map((point) => ({ x: point.x, y: point.y })),
        isClosed: true
      }))
      drawAllShapes()
      canvas.value.addEventListener('mousedown', onMouseDown)
      canvas.value.addEventListener('mousemove', onMouseMove)
      canvas.value.addEventListener('mouseup', onMouseUp)
      canvas.value.addEventListener('mouseleave', onMouseUp)
      canvas.value.addEventListener('click', onClick)
      canvas.value.addEventListener('contextmenu', onContextMenu)
    }
  } catch (error) {
    console.error('获取 PDF 列表时出错:', error)
  }
}
onMounted(() => {
  fetchData(page.value)
})

onBeforeUnmount(() => {
  if (canvas.value) {
    canvas.value.removeEventListener('mousedown', onMouseDown)
    canvas.value.removeEventListener('mousemove', onMouseMove)
    canvas.value.removeEventListener('mouseup', onMouseUp)
    canvas.value.removeEventListener('mouseleave', onMouseUp)
    canvas.value.removeEventListener('click', onClick)
    canvas.value.removeEventListener('contextmenu', onContextMenu)
  }
})

function onMouseDown(event: MouseEvent) {
  if (isDrawingEnabled.value) return

  if (canvas.value) {
    const rect = canvas.value.getBoundingClientRect()
    const x = event.clientX - rect.left
    const y = event.clientY - rect.top

    const pointInfo = getPointAtPosition(x, y)
    if (pointInfo) {
      isDraggingPoint.value = true
      draggedPoint.value = pointInfo
    } else {
      const shapeIndex = getShapeAtPosition(x, y)
      if (shapeIndex !== null) {
        isDraggingShape.value = true
        draggedShapeIndex.value = shapeIndex
      }
    }
    lastMousePos.value = { x, y }
  }
}

function onMouseMove(event: MouseEvent) {
  if (canvas.value) {
    const rect = canvas.value.getBoundingClientRect()
    const x = event.clientX - rect.left
    const y = event.clientY - rect.top

    currentMousePos.value = { x, y } // 更新当前鼠标位置
    drawAllShapes() // 每次移动时都重绘
  }
  if (canvas.value && lastMousePos.value) {
    const rect = canvas.value.getBoundingClientRect()
    const x = event.clientX - rect.left
    const y = event.clientY - rect.top

    currentMousePos.value = { x, y }
    const dx = x - lastMousePos.value.x
    const dy = y - lastMousePos.value.y

    if (isDraggingPoint.value && draggedPoint.value) {
      const { shapeIndex, pointIndex } = draggedPoint.value
      shapes.value[shapeIndex].points[pointIndex].x += dx
      shapes.value[shapeIndex].points[pointIndex].y += dy
      drawAllShapes()
    } else if (isDraggingShape.value && draggedShapeIndex.value !== null) {
      const points = shapes.value[draggedShapeIndex.value].points
      points.forEach((point) => {
        point.x += dx
        point.y += dy
      })
      drawAllShapes()
      updateCoordinates()
    }
    lastMousePos.value = { x, y }
  }
}

function onMouseUp(event: MouseEvent) {
  isDraggingPoint.value = false
  isDraggingShape.value = false
  draggedPoint.value = null
  draggedShapeIndex.value = null
  lastMousePos.value = null
  updateCoordinates()
}

function onClick(event: MouseEvent) {
  if (!isDrawingEnabled.value) return

  if (event.button === 0) {
    if (ctx.value) {
      if (!currentShape.value || currentShape.value.isClosed) {
        currentShape.value = { points: [], isClosed: false }
        shapes.value.push(currentShape.value)
        isDrawingLine.value = true
        showGuidelines.value = true
      }
      if (!currentShape.value.isClosed) {
        const rect = canvas.value!.getBoundingClientRect()
        const x = event.clientX - rect.left
        const y = event.clientY - rect.top
        currentShape.value.points.push({ x, y })
        drawAllShapes()
      }
    }
  }
}

function onContextMenu(event: MouseEvent) {
  event.preventDefault()

  if (isDrawingEnabled.value) {
    if (ctx.value && currentShape.value && !currentShape.value.isClosed) {
      if (currentShape.value.points.length >= 3) {
        currentShape.value.isClosed = true
        isDrawingLine.value = false
        showGuidelines.value = false
        drawAllShapes()
        updateCoordinates()
      } else {
        alert('需要至少 3 个点才能闭合图案')
      }
    }
  } else {
    if (canvas.value) {
      const rect = canvas.value.getBoundingClientRect()
      const x = event.clientX - rect.left
      const y = event.clientY - rect.top

      const shapeIndex = getShapeAtPosition(x, y)
      if (shapeIndex !== null) {
        shapes.value.splice(shapeIndex, 1)
        drawAllShapes()
        updateCoordinates()
      }
    }
  }
}
function handleMouseEnter(shapeIndex: number) {
  //鼠标移入时相应图形背景色变为半透明绿色
  hoveredShapeIndex.value = shapeIndex
  drawAllShapes()
}
function handleMouseLeave(shapeIndex: number) {
  //鼠标移出时变为透明色
  hoveredShapeIndex.value = null
  drawAllShapes()
}
// 更新 coordinates 示例
function updateCoordinates() {
  coordinates.value = shapes.value
    .filter((shape) => shape.points.length >= 3)
    .map((shape, shapeIndex) => ({
      shapeIndex,
      audio:shape.audio || '',
      points: shape.points.map((point) => ({ x: point.x, y: point.y }))
    }))
  appGaphicalData.graphicaValue(coordinates.value)
}
function drawAllShapes() {
  if (ctx.value && canvas.value) {
    ctx.value.clearRect(0, 0, canvas.value.width, canvas.value.height)

    shapes.value.forEach((shape, index) => {
      const points = shape.points
      if (points.length >= 2) {
        ctx.value!.beginPath()
        ctx.value!.moveTo(points[0].x, points[0].y)
        for (let i = 1; i < points.length; i++) {
          ctx.value!.lineTo(points[i].x, points[i].y)
        }
        if (shape.isClosed) {
          ctx.value!.closePath()
        }
        ctx.value!.fillStyle =
          hoveredShapeIndex.value === index ? 'rgba(0, 255, 0, 0.5)' : 'transparent'
        ctx.value!.fill()
        ctx.value!.strokeStyle = hoveredShapeIndex.value === index ? 'rgba(0, 255, 0, 0.5)' : 'red'
        ctx.value!.lineWidth = 2
        ctx.value!.stroke()
      }
    })
    // 获取最后一个已存在的点
    const lastShape = shapes.value[shapes.value.length - 1]
    const lastPoint = lastShape ? lastShape.points[lastShape.points.length - 1] : null

    if (lastPoint && showGuidelines.value) {
      const { x, y } = lastPoint
      ctx.value.strokeStyle = 'rgba(144, 238, 144, 0.5)' // 淡绿色
      ctx.value.lineWidth = 1

      // 绘制水平线
      ctx.value.beginPath()
      ctx.value.moveTo(0, y)
      ctx.value.lineTo(canvas.value.width, y)
      ctx.value.stroke()

      // 绘制垂直线
      ctx.value.beginPath()
      ctx.value.moveTo(x, 0)
      ctx.value.lineTo(x, canvas.value.height)
      ctx.value.stroke()
    }

    // 绘制虚线从上一个点到当前鼠标位置
    if (
      isDrawingLine.value &&
      currentMousePos.value &&
      currentShape.value &&
      currentShape.value.points.length > 0
    ) {
      const lastPoint = currentShape.value.points[currentShape.value.points.length - 1]
      const dx = currentMousePos.value.x - lastPoint.x
      const dy = currentMousePos.value.y - lastPoint.y
      const distance = Math.sqrt(dx * dx + dy * dy)
      const slope = Math.abs(dy / dx) || Infinity

      // 根据斜率和距离设置虚线样式
      if (slope > 2 || distance < 10) {
        // 斜率接近垂直或距离很短时为实线
        ctx.value!.strokeStyle = 'green'
        ctx.value!.beginPath()
        ctx.value!.moveTo(lastPoint.x, lastPoint.y)
        ctx.value!.lineTo(currentMousePos.value.x, currentMousePos.value.y)
        ctx.value!.lineWidth = 2
        ctx.value!.stroke()
      } else {
        ctx.value!.setLineDash([5, 5]) // 设置虚线
        ctx.value!.strokeStyle = 'gray' // 虚线颜色
        ctx.value!.beginPath()
        ctx.value!.moveTo(lastPoint.x, lastPoint.y)
        ctx.value!.lineTo(currentMousePos.value.x, currentMousePos.value.y)
        ctx.value!.stroke()
        ctx.value!.setLineDash([]) // 重置为实线
      }
    }
  }
}
function getPointAtPosition(
  x: number,
  y: number
): { shapeIndex: number; pointIndex: number } | null {
  for (let i = shapes.value.length - 1; i >= 0; i--) {
    const shape = shapes.value[i]
    for (let j = 0; j < shape.points.length; j++) {
      const point = shape.points[j]
      const dx = x - point.x
      const dy = y - point.y
      const distance = Math.sqrt(dx * dx + dy * dy)
      if (distance <= 5) {
        return { shapeIndex: i, pointIndex: j }
      }
    }
  }
  return null
}

function getShapeAtPosition(x: number, y: number): number | null {
  for (let i = shapes.value.length - 1; i >= 0; i--) {
    const shape = shapes.value[i]
    if (isPointInShape(x, y, shape)) {
      return i
    }
  }
  return null
}

function isPointInShape(
  x: number,
  y: number,
  shape: { points: { x: number; y: number }[]; isClosed: boolean }
): boolean {
  if (!shape.isClosed) return false
  let inside = false
  const points = shape.points
  for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
    const xi = points[i].x,
      yi = points[i].y
    const xj = points[j].x,
      yj = points[j].y

    const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi
    if (intersect) inside = !inside
  }
  return inside
}
</script>
